package com.bj58.ecdata.mailreport.threads;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;

import javax.mail.internet.InternetAddress;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.bj58.ecdata.mailreport.cache.ServiceBeanFactory;
import com.bj58.ecdata.mailreport.controllers.IntEmailAddController;
import com.bj58.ecdata.mailreport.db.CommonDao;
import com.bj58.ecdata.mailreport.db.DBConfig;
import com.bj58.ecdata.mailreport.model.IEAdd;
import com.bj58.ecdata.mailreport.model.IEMain;
import com.bj58.ecdata.mailreport.model.IESend;
import com.bj58.ecdata.mailreport.model.IESendRecord;
import com.bj58.ecdata.mailreport.model.IETable;
import com.bj58.ecdata.mailreport.model.IETableSearch;
import com.bj58.ecdata.mailreport.model.TaskLog;
import com.bj58.ecdata.mailreport.service.impl.IEAddService;
import com.bj58.ecdata.mailreport.service.impl.IEMainService;
import com.bj58.ecdata.mailreport.service.impl.IESendService;
import com.bj58.ecdata.mailreport.service.impl.IETableSearchService;
import com.bj58.ecdata.mailreport.service.impl.IETableService;
import com.bj58.ecdata.mailreport.templateParse.IETemplateParse;
import com.bj58.ecdata.mailreport.util.DateUtil;
import com.bj58.ecdata.mailreport.util.MailUtil;
import com.bj58.ecdata.mailreport.util.MyPathUtil;
import com.bj58.ecdata.mailreport.util.SubmitSms;
import com.bj58.ecdata.mailreport.util.VelocityTools;
import com.bj58.groupbuy.service.locator.ServiceLocator;
import com.bj58.groupbuy.util.PathUtil;

/**自定义邮件发送
 * @author huangliang
 *
 */
public class IESendTimerTask  extends TimerTask{
	private static Logger log4j = Logger.getLogger("IESendTaskLog");
	private static VelocityTools vt = new VelocityTools();
	private CommonDao comDao = new CommonDao();
	private IEMainService IEMainService = (IEMainService) ServiceBeanFactory.get(IEMain.class, IEMainService.class);
	private IESendService IESendService = (IESendService) ServiceBeanFactory.get(IESend.class,IESendService.class);
	private IETableService IETableService = (IETableService) ServiceBeanFactory.get(IETable.class,IETableService.class);
	private IETableSearchService IETableSearchService = (IETableSearchService) ServiceBeanFactory.get(IETableSearch.class,IETableSearchService.class);
	private IEAddService ieAddService = (IEAddService) ServiceBeanFactory.get(IEAdd.class,IEAddService.class);
	private static IntEmailAddController ieAddController=new IntEmailAddController();
	private static IETemplateParse templateParse = new IETemplateParse();
	private String searchDate = null;
	private String nowTime = null;	//启动时间
	private IESend sendVo;
	private boolean isTest=false;	//是否是测试，如果是测试执行则不能更新数据库。
	private IESendRecord sendRecord = new IESendRecord();
	private String testSendName=null;
	static{
		 PropertyConfigurator.configure(PathUtil.getWFNamespaceFolder()+"/IESendlog4j.properties");
	}
	public IESendTimerTask(IESend sendVo,String serchDate,boolean isTest,String testSendName){
		this.sendVo = sendVo;
		this.isTest=isTest;
		if(isTest)
			this.testSendName = testSendName;	//测试发送或者补发时邮件名可以自己定
		if(serchDate!=null && !"".equals(serchDate))
			this.searchDate=DateUtil.strToStr(serchDate, "yyyy-MM-dd");
		else
			searchDate=DateUtil.getStringDate(DateUtil.getDateBefore(new Date(), 1), "yyyy-MM-dd"); //查询日期默认为发送日期前一天
	}	
	
	
	@Override
	public void run() {
		try {
			nowTime = DateUtil.getStringDate(new Date(), "HH:mm:ss");
			IESend newSend = IESendService.getById(sendVo.getId());
			//检查发送时sendVo是否已经失效了
			if(newSend ==null ){
				return;
			}else if(!isTest && (newSend.getState() != sendVo.getState() || newSend.getSendState() != sendVo.getSendState())){	//准备发送时，当前状态已经被人为修改过
				if(newSend.getSendState() == sendVo.getSendState()){
					newSend.setSendState(0);
				}
				newSend.setUpdateTime(DateUtil.getStringDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
				IESendService.updateVo(newSend);//更新状态
				return;
			}
			sendRecord.setSendTime(DateUtil.getStringDate(new Date(), "yyyy-MM-dd HH:mm:ss"));   //发送记录
			if(sendMail(sendVo)){
				sendRecord.setSendState("成功");
			}else{
				sendRecord.setSendState("失败");
			}
		} catch (Exception e) {
			IESendTimerTask.getLogger().error("自定义邮件发送出错!", e);
			mailWarn(sendVo,e);
			sendRecord.setSendState("失败");
		}finally{
			try {
				if(!isTest){	//不是测试，需要更新数据库状态,如果是测试，则数据发送状态不会更新。
					sendRecord.setSendType("自动");
					sendVo.setUpdateTime(DateUtil.getStringDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
        			IESendService.updateVo(sendVo);
				}else {
					sendRecord.setSendType("手动");
				}
				IESendService.saveSendRecord(sendRecord);
			} catch (Exception e) {
    			IESendTimerTask.getLogger().error("自定义报错短信或邮件发送出错!", e);
    		}
		}
	}

	
	/**发送
	 * step1：取出对应的ie_main ; ie_table ; ie_ie_tablesearch
	 * step2：根据templatetype获取对应模板
	 * step3：查询数据
	 * step4：根据模板类型及表格设置 计算特殊数据 （如公式指标 、同比、环比）
	 * step5：渲染、发送
	 */
	private boolean sendMail(IESend ieSend)throws Exception{
		ieSend.setAbnormalTd(0);
		ieSend.setTdNum(0);
		IEMain ieMain = IEMainService.getByCondition(" id = " + ieSend.getMainId()).get(0);
		if(isTest){	//测试发送或者补发时邮件名可以自己定
			ieMain.setEmailName(testSendName);
		}
		sendRecord.setSendId(ieSend.getId());
		sendRecord.setEmailName(ieMain.getEmailName());
		sendRecord.setEmailAds(ieSend.getEmailAds());
		//------ 数据源校验 ---------
		IESendTimerTask.getLogger().info("开始数据源校验 : "+ieMain.getEmailName());
		String checkMessage  = checkSrcTable(ieSend.getMainId());
		IESendTimerTask.getLogger().info("结束数据源校验 : "+ieMain.getEmailName());
		
		if(!"true".equals(checkMessage)){
			srcTableWarn(checkMessage);
			return false;
		}
		//发送中
		if(!isTest){
			sendVo.setSendState(2);
			IESendService.updateVo(sendVo);
		}
		List<IETable> ieTableList = IETableService.getListByCon(" state = 1 and  mainId = " +  ieSend.getMainId()+" order by sequence");
		//先获取模板主体
		Map<String,Object> conMap = new HashMap<String,Object>();
		String main = MyPathUtil.getTemByPath(IETemplateParse.templateMainPath);
		
		//邮件内各表内容
		StringBuffer sendContent = new StringBuffer();
		//手动编写的附加内容放在前面
		String addContent="";
		IESendTimerTask.getLogger().info("开始发送 : "+ieMain.getEmailName());
		IEAdd ieAddVo=ieAddService.getByMainId(ieSend.getMainId());
		if(ieAddVo !=null){
			Class<IntEmailAddController> ieAddClass=IntEmailAddController.class;
			Method method=ieAddClass.getDeclaredMethod(ieAddVo.getMethod(), String.class,String.class);
			String filepath =PathUtil.getWFNamespaceFolder()+"/intEmail/"+ieAddVo.getTemplateName();
			addContent=(String) method.invoke(ieAddController, searchDate,filepath);
		}
		
		int counter=1;
		for (IETable ieTable : ieTableList) {
			try{
			List<IETableSearch> ieTablerSearchList = IETableSearchService.getListByCon(" state = 1 and tableId = " + ieTable.getId());
			int templateType = ieTable.getTemplateType();
			if(ieAddVo !=null && (ieAddVo.getType()+1)==counter){
				sendContent.append(addContent);
				addContent="";
			}
			String content = "";
			switch (templateType) {
			case 1: //汇总模板
				content = templateParse.getTemplateContentOne(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			case 2: //详细指标模板
				content = templateParse.getTemplateContentTwo(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			case 3://分城市指标模板(二维分组)
				content = templateParse.getTemplateContentThree(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			case 4: //总体(分列展现)指标模板
				content = templateParse.getTemplateContentFour(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			case 5: //分行指标模板
				content = templateParse.getTemplateContentFive(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			case 6: //分行分列指标模板
				content = templateParse.getTemplateContentSix(ieSend, ieTable, ieTablerSearchList, searchDate);
				sendContent.append(content);
				break;
			}
			
			counter++;
			}catch(Exception e){
				IESendTimerTask.getLogger().error(ieTable.getTableName());
				throw new Exception(ieTable.getTableName(),e);
			}
		}
		
		sendContent.append(addContent); //添加附加内容，若没有或者已经添加在顶部则为"";
		conMap.put("sendContent",sendContent.toString());  //邮件内容
		String content  = VelocityTools.render(main, conMap);
		if(ieSend.getTdNum()>0 && 1.0*ieSend.getAbnormalTd()/ieSend.getTdNum()>ieSend.getThreshold0() && !isTest){
			mailWarn(ieMain,ieSend,content);
			ieSend.setSendState(4);   //预警
		}
		else{
			int sendResult = MailUtil.javaMailSend(ieMain.getEmailName() + " " + searchDate, content, ieSend.getEmailAds(), null);
			if(sendResult != 1){	//发送失败，邮箱或者网络错误，先发给自己试一试。
				int sendToAdminResult = MailUtil.javaMailSend(ieMain.getEmailName() + " " + searchDate, content, "huangliang@58.com;guoxue@58.com", null);
				if(sendToAdminResult == 1){	//发送自己成功，说明收件人中有邮箱失效，需手动执行或者将已收到邮件转发出去。
					ieSend.setSendState(5);  //状态5表示等待人工处理
				}
				throw new com.sun.mail.smtp.SMTPAddressFailedException(new InternetAddress(), content, 550, "Could not connect to SMTP host OR 550 Mailbox not found!");
			}else{
				ieSend.setSendState(1);  //成功
			}
		}
		IESendTimerTask.getLogger().info("结束发送 : "+ieMain.getEmailName());
		return true;
	}
	
	private String checkSrcTable(int mainID)throws Exception{
		String day = searchDate.replace("-", ""); //yyyyMMdd
		String month = searchDate.substring(0,searchDate.lastIndexOf("-")).replace("-", ""); ///yyyyMM
		String year =  searchDate.substring(0,searchDate.indexOf("-")); ///yyyy
		String conString = " mainId=" +mainID+" and isCheck=1 and isSpe=0 and srcTable is not null and state = 1 group by dbId,srcTable" ;
		List<IETableSearch> srcTableList = IETableSearchService.getListByCon(conString);
		String sql = "select run_date,db_name,tb_name,exe_type,status,result_count from task.v_task where run_date='%s' and db_host='%s' and tb_name='%s'";
		
		String lastDay = DateUtil.strToStrBefore(searchDate,"yyyy-MM-dd",1);
		String lastWeek = DateUtil.strToStrBefore(searchDate,"yyyy-MM-dd",7);
		String dataArr[] = {searchDate,lastDay,lastWeek};
		for(IETableSearch ieSearch : srcTableList){
			String tableName = ieSearch.getSrcTable();
			String dbName = ieSearch.getDbName();
			String dbHost = ieSearch.getDbHost();
			IESendTimerTask.getLogger().info("begin check table "+dbName+"."+tableName);

			for(String date : dataArr){
				boolean hasData = false;
				int dbID = ieSearch.getDbId();
				String status = null;
				//查任务调度执行结果日志
				List<TaskLog> runLog = comDao.getAllColsByVo(String.format(sql,date,dbHost,tableName), TaskLog.class, DBConfig.db_manage.getIntType());
				if(runLog == null || runLog.isEmpty()){
					//没有日志，直接查库
					String dateFiled = ieSearch.getDateField();
					if(StringUtils.isBlank(dateFiled))
						dateFiled = "stat_date";
					String sql2 = "select count(1) from " + tableName.replace("#Ymd#", day).replace("#Ym#", month).replace("#Y#", year)+" where "+dateFiled+"='"+searchDate+"'";
					int count = comDao.countAll(sql2, dbID);
					if(count>0)
						hasData = true;
				}else{
					TaskLog taskLog = runLog.get(0);
					status = taskLog.getStatus();
					if("success".equals(status))
						hasData = true;
				}
				
				if(hasData == false)
					return dbName+"."+tableName+" : "+status; 
			}
		}
		return "true";
	}

	/**
	 * 数据源错误邮件报告
	 * @param mainVo
	 * @param ieSend
	 * @param content
	 */
	private void srcTableWarn(String content){
		try {
			//校验发送时间
			String sendTime = sendVo.getSendTime();
			
			//设定的邮件发送时间到当前时间的秒数。
			long minutes = (DateUtil.strToDate(nowTime, "HH:mm:ss").getTime() - DateUtil.strToDate(sendTime, "HH:mm:ss").getTime()) / (60*1000) ;
			//每30分钟才发邮件提示，否则频率太高
			if(((minutes / 5) % 6 == 0) || isTest){ 	
				sendVo.setSendState(6);	//设置"等待"状态
				String title = "【提示】"+sendRecord.getEmailName()+" 数据源校验失败!";
				String warnContent = "<br>邮件名：" + sendRecord.getEmailName() + " "+searchDate+"。<br>send id 为：" +sendRecord.getSendId() 
						+"。<br>出错数据："+content;
	        	//邮件报警, 由于发送频率过高,不发送短信骚扰
				warnContent+="。 <br>邮件收件人："+sendRecord.getEmailAds();
				MailUtil.javaMailSend(title, warnContent ,sendVo.getWarnMail(), null);
			}

		} catch (Exception e) {
			IESendTimerTask.getLogger().error("自定义报错短信或邮件发送出错!", e);
		}
	}
	
	
	/**
	 * 程序执行错误报警
	 */
	private void mailWarn(IESend vo ,Exception excep){
		try {
			//校验发送时间
			String sendTime = sendVo.getSendTime();
			
			//设定的邮件发送时间到当前时间的秒数。
			long minutes = (DateUtil.strToDate(nowTime, "HH:mm:ss").getTime() - DateUtil.strToDate(sendTime, "HH:mm:ss").getTime()) / (60*1000) ;

			IEMain mainVo = IEMainService.getById(vo.getMainId());
			//每30分钟才发提示，否则频率太高
			if(((minutes / 5) % 6 == 0) || isTest){
				String title = "自定义邮件发送程序报错!";
				String warnContent = "\n邮件名：" + mainVo.getEmailName() + " "+searchDate+"。\nsend id 为：" + vo.getId()+"。\n出错数据："+excep.getMessage();
				//短信报警
				Map<String,String> sendMap = new HashMap<String, String>();
	        	for(String tel:vo.getWarnTel().split(";")){
	        		sendMap.put(StringUtils.trim(tel), title + warnContent);
	        	}
	        	SubmitSms.submitMsg(sendMap);
	        	//邮件报警
				StringWriter writer=new StringWriter();
				excep.printStackTrace(new PrintWriter(writer));
				writer.close();
				warnContent+="。 <br>收件人："+vo.getEmailAds();
				warnContent+="。 <br>"+writer;
				MailUtil.javaMailSend(title, warnContent.replace("\n", "<br>"),"huangliang@58.com;guoxue@58.com", null);
			}
			if(sendVo.getSendState() != 5)	//5表示待人工处理的错误，特殊
				sendVo.setSendState(3);	//设置"失败"状态
        } catch (Exception e) {
			IESendTimerTask.getLogger().error(DateUtil.getStringDate(new Date(), "yyyy-MM-dd HH:mm:ss")+"--报错短信或邮件发送出错!", e);
		}
	}
	
	/**
	 * 数据异常阈值预警
	 * @param mainVo
	 * @param ieSend
	 * @param content
	 */
	private void mailWarn(IEMain mainVo,IESend ieSend,String content){
		try {
			String title = "[警报]自定义邮件预警!";
			String warnContent = "预警邮件名：" + mainVo.getEmailName() + " "+searchDate+"\n。send id 为：" + ieSend.getId();
			//短信报警
			Map<String,String> sendMap = new HashMap<String, String>();
//        	sendMap.put("13522482231", title + warnContent);
//        	sendMap.put("13811583416", title + warnContent);
        	for(String tel:ieSend.getWarnTel().split(";")){
        		sendMap.put(StringUtils.trim(tel), title + warnContent);
        	}
        	SubmitSms.submitMsg(sendMap);
			
			warnContent+="。  <br>异常数据比率: "+vt.compRateDirect(ieSend.getAbnormalTd(), ieSend.getTdNum());
			warnContent+="。   <br>当前阈值为: "+vt.comRate(String.valueOf(ieSend.getThreshold0())) +",+/-"+ieSend.getThreshold();
			MailUtil.javaMailSend(title, warnContent+"<br>"+content,ieSend.getWarnMail(), null);
			
		} catch (Exception e) {
			IESendTimerTask.getLogger().error("自定义报错短信或邮件发送出错!", e);
		}
	}
	
	public static Logger getLogger(){
		return log4j;
	}
	
	public static void main(String[] args) {
		try {
			IESendService IESendService = ServiceLocator.get(IESendService.class);
			
			String searchDate = "2014-10-26";
			IESend ieSend = IESendService.getById(221);
			ieSend.setEmailAds("guoxue@58.com");
			IESendTimerTask sendTask=new IESendTimerTask(ieSend,searchDate,true,"测试");
			Thread sendThread=new Thread(sendTask);
			sendThread.start();
		} catch (Exception e) {
			IESendTimerTask.getLogger().error("自定义报错短信或邮件发送出错!", e);
		}
	}
	
}
